

#ifndef __packet_h__
#define __packet_h__
#define NOMINMAX
#include <algorithm>
#include <string>
#include <cstring>
#include <ostream>
#include <istream>
#include <iomanip>
#include <assert.h>

class packet
{
public:
	static const unsigned short MAX_SIZE = 1024;
	typedef char                  data_type;
	typedef data_type*           ptr_type;
	typedef const data_type*     const_ptr_type;
	typedef data_type&           ref_type;
	typedef const data_type&    const_ref_type;
	typedef unsigned short       size_type;

	packet(): size_(0), put_(0), get_(0) 
	{
	}

	packet(const_ptr_type ptr, size_type n) : size_(n), put_(n), get_(0)
	{
		assert(n<MAX_SIZE);
		std::memcpy(buffer,ptr,n);
	}

	explicit packet(const_ptr_type str)  : get_(0)
	{
		size_=static_cast<unsigned short>(std::strlen(str));
		assert(size_<MAX_SIZE);
		put_=size_;
		std::memcpy(buffer,str,size_);
	}

	explicit packet(const std::string& str): get_(0)
	{
		size_=static_cast<unsigned short>(str.length());
		assert(size_<MAX_SIZE);
		put_=size_;
		std::memcpy(buffer,str.data(),size_);
	}

	packet(const packet& in) : size_(in.size_), put_(in.size_), get_(0)
	{
		assert(size_<MAX_SIZE);
		std::memcpy(buffer,in.buffer,size_);
	}

	packet& operator=(const packet& in)
	{
		assert(in.size_<MAX_SIZE);
		size_=in.size_;
		put_=size_;
		get_=0;
		std::memcpy(buffer,in.buffer,size_);
		return *this;
	}

	inline size_type Size() const { return size_; }
	inline const_ptr_type Data() const { return buffer; }

	size_type Read(ptr_type out, size_type n) const
	{
		n = std::min(static_cast<size_type>(size_-get_),n);
		std::memcpy(out,buffer+get_,n);
		get_+=n;
		return n;
	}

	size_type Read(std::string& out, size_type n=0) const
	{
		if(n) {
			n=std::min(static_cast<size_type>(size_-get_),n);
		}
		else {
			n=size_-get_;
		}
		out.assign(buffer+get_,n);
		get_+=n;
		return n;
	}

	size_type Write(const_ptr_type in, size_type n)
	{
		n=std::min(static_cast<size_type>(MAX_SIZE-put_),n);
		int overflow = n-(size_-put_);
		if(overflow) size_+=overflow;
		std::memcpy(buffer+put_,in,n);
		put_+=n;
		return n;
	}

	size_type Write(const std::string& in)
    {
		return Write(in.data(), in.length());
	}

	inline size_type TellPut() { return put_; }
	inline size_type TellGet() { return get_; }

	enum SeekMode	{	Curr,Begin,DataEnd,BufferEnd };
	void SeekPut(int delta, SeekMode mode=Curr)
	{
		if(mode==Begin) put_=0;
		else if(mode==DataEnd) put_=size_;
		else if(mode==BufferEnd) put_=MAX_SIZE;

		if(delta<0) delta=std::max(delta,-put_);
		put_+=delta;
		if(put_>MAX_SIZE) put_=MAX_SIZE;
	}
	void SeekGet(int delta, SeekMode mode=Curr)
	{
		if(mode==Begin) get_=0;
		else if(mode==DataEnd) get_=size_;
		else if(mode==BufferEnd) get_=MAX_SIZE;
		if(delta<0) delta=std::max(delta,-get_);
		get_+=delta;
		if(get_>MAX_SIZE) get_=MAX_SIZE;
	}

	void Clear() { size_=0; get_=0; put_=0; }
	ref_type operator[](size_type n)
	{
		assert(n<MAX_SIZE);
		return buffer[n];
	}

	const_ref_type operator[](size_type n) const
	{
		assert(n<MAX_SIZE);
		return buffer[n];
	}
private:
	data_type buffer[MAX_SIZE];
	size_type size_;
	size_type put_;
	mutable size_type get_;
};


///////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////


template<class T>
inline packet& operator<<(packet& p, const T& val)
{
	p.Write(reinterpret_cast<packet::const_ptr_type>(&val),sizeof(T));
	return p;
}

inline packet& operator<<(packet& p, packet::const_ptr_type str)
{
	p.Write(str,static_cast<packet::size_type>(std::strlen(str)));
	return p;
}

inline packet& operator<<(packet& p, const std::string& str)
{
	p.Write(str.data(),str.length());
	return p;
}

template<class T>
inline const packet& operator>>(const packet& p, T& val)
{
	p.Read(reinterpret_cast<packet::ptr_type>(&val),sizeof(T));
	return p;
}

inline const packet& operator>>(const packet& p, std::string& str)
{
	p.Read(str,0);
	return p;
}

inline std::ostream& operator<<(std::ostream& out, packet& p)
{
	out.write(p.Data(), p.Size());
	return out;
}

inline std::istream& operator>>(std::istream& in, packet& p)
{
	std::string tmp;
	in>>tmp;
	p.Write(tmp);
	return in;
}

#endif //__packet_h__
